#include "App.hpp"
#include "Savings.hpp"
#include "HuffTree.hpp"
#include <cstdlib>
#include <algorithm>
#include "BasicSolver.hpp"
#include "BasicReverseSolver.hpp"
#include "Merger.hpp"
#include "NiceTrySolver.hpp"
#include "NiceTry2Solver.hpp"
#include "VNDTry.hpp"
#include "NiceMergeSolver.hpp"
#include "RandomSolver.hpp"
#include "MultiStart.hpp"
#include "SimulatedAnnealing.hpp"
#include "PSOParticle.hpp"
#include "PSO.hpp"
#include "Sol.hpp"
#include "Moves.hpp"
#include "Solvers.hpp"

// soit c0 -> c1 tq c1 plus proche voisin de c0
//  si on a c0.(x.ARRIVAL) + c0.service_time > DUE_DATE et c1.(x.ARRIVAL) + c0.service_time > DUE_DATE

//  alors   c1.ARRIVAL = c0.ARRIVAL + c0.service_time + (c0,c1)
//          c0.ARRIVAL = inconnue
// si c0.ARRIVAL existe

// si c1.ARRIVAL

App::App()
{
    apVRPTW = 0;    
}

App::~App()
{
    delete apVRPTW;
}


int     App::exec(int argc, char** argv)
{
    try
    {
        std::deque<unsigned>        huff_base(0x200, 0xFFFF);
        std::deque<unsigned>        huff_base2(0x200);
        std::deque<unsigned>		huff_from_sol;
        std::deque<unsigned>        base;
        
        std::multimap<double, unsigned>     clients_by_due;
        std::multimap<double, unsigned>     clients_sort;
        std::multimap<double, unsigned>::reverse_iterator   it;
        std::multimap<double, unsigned>::iterator           it2;
        std::multimap<double, unsigned>::const_iterator     is;
        std::map<unsigned, unsigned>        clients_served;
        SolutionEval						soleval;
        
        
        if (argc != 2)
        {
            std::cerr << "prog usage : ./prog instance.txt" << std::endl;
            std::cerr << "(with format specified inside DIR './instances/' please)" << std::endl;
            EXC_THROW("erreur dans les arguments");
        }
        
        try
        {
            apVRPTW = new VRPTW(argv[1]);
        }
        catch(std::bad_alloc&)
        {   EXC_THROW("unable to allocate vrptw");  }
        
        apVRPTW->print();
        
        
        Moves::buildInstance(*apVRPTW);
        
        
        PSO			pso(*apVRPTW,
						100,
						100,
                        5,
						std::pair<double, double>(0.9, 0.1),
						2.,
						2.,
						1.5,
						1.5,
						1000,
						std::pair<double, double>(0., 100.),
						std::pair<double, double>(0., 100.));
		
		std::cout << "calling pso.execute()" << std::endl;
		pso.execute();
        return (0);
        
        Sol		    s(*apVRPTW);
        Sol::Eval   seval;
        VND			vnd(*apVRPTW);
        BasicInsert	binsert(*apVRPTW);
        std::vector<unsigned char>	sequence(apVRPTW->size());
        
        for (unsigned i = 0 ; i < apVRPTW->size() ; i++)
			sequence[i] = i;
        
        //random_shuffle(sequence.begin(), sequence.end());
        
        //binsert.execute(&sequence);
        //s = binsert.getResult();
        
        //std::cout << s.toString() << std::endl;
        //return (0);
        
        //vnd.execute(&s);
        //return (0);
        
        for (unsigned i = 0 ; i < apVRPTW->size() ; i++)
        {
			s.getrVehicle(i).insert(i, s[i].bdepot().info().siClient, s[i].edepot().info().siClient);
		}
        
        std::cout << s.toString() << std::endl;
        
        
        vnd.execute(&s);
        return (0);
        
        
        
        
        
        std::cout << apVRPTW->checkInsert2((s.getrVehicle(0).iterator() = apVRPTW->size()).info(), (s.getrVehicle(0).iterator() = apVRPTW->size() + 1).info(), 0, s.getrVehicle(0).edepot().info().siCCapacityAtArrival) << std::endl;
        s.getrVehicle(0).insert(0, apVRPTW->size(), apVRPTW->size() + 1);
        std::cout << s.getrVehicle(0).toString() << std::endl;
        
        s.eval(seval);
        std::cout << seval.toString() << std::endl;
        
        //return (0);
        
        //s.getrVehicle(0).insert(4, apVRPTW->size(), 0);
        
        std::cout << s.getrVehicle(0).toString() << std::endl;
        std::cout << "insert" << std::endl;
        std::cout << apVRPTW->checkInsert2((s.getrVehicle(0).iterator() = 4).info(), (s.getrVehicle(0).iterator() = 0).info(), 3, s.getrVehicle(0).edepot().info().siCCapacityAtArrival) << std::endl;
        //s.getrVehicle(0).insert(3, 4, 0);
        std::cout << s.getrVehicle(0).toString() << std::endl;
        std::cout << "insert" << std::endl;
        std::cout << apVRPTW->checkInsert2((s.getrVehicle(0).iterator() = 4).info(), (s.getrVehicle(0).iterator() = 3).info(), 1, s.getrVehicle(0).edepot().info().siCCapacityAtArrival) << std::endl;
        //s.getrVehicle(0).insert(1, 4, 3);
        
        unsigned    ii = 3;
        
        for (unsigned i = 5 ; i < 100 ; i++)
        {
            VRPTW::RealizableStatus    status;
            
            std::cout << "insert" << std::endl;
            std::cout << (int)(status = apVRPTW->checkInsert2((s.getrVehicle(0).iterator() = 4).info(), (s.getrVehicle(0).iterator() = ii).info(), i, s.getrVehicle(0).edepot().info().siCCapacityAtArrival)) << std::endl;
            if (status == VRPTW::REALIZABLE_LTE)
            {
                s.getrVehicle(0).insert(i, 4, ii);
                ii = i; 
                std::cout << s.getrVehicle(0).toString() << std::endl;
            }
        }
        
        //s.getrVehicle(0).insert(2, apVRPTW->size(), 1);
        
        //std::cout << apVRPTW->checkInsert((s.getrVehicle(0).iterator() = apVRPTW->size()).info(), (s.getrVehicle(0).iterator() = 2).info(), 3) << std::endl;
        
        //s.getrVehicle(0).insert(3, apVRPTW->size(), 2);
        
        //std::cout << apVRPTW->checkInsert((s.getrVehicle(0).iterator() = apVRPTW->size()).info(), (s.getrVehicle(0).iterator() = 3).info(), 4) << std::endl;
        
        //s.getrVehicle(0).insert(4, apVRPTW->size(), 3);
        std::cout << s.toString() << std::endl;
        
        return (0);
        
        Solver*		solver = 0;
        Solver*		rsolver = 0;
        Merger*		msolver = 0;
        Solver*		ntsolver = 0;
        VNDTry*     vndsolver = 0;
        Solver*     nt2solver = 0;
        Solver*     nmsolver = 0;
        Solution    sol(*apVRPTW);
        Solver*     sa = 0;
        

        /*for (unsigned i = 0 ; i < 2 ; i++)
        {
			PSOParticle					particle(*apVRPTW);
			particle.decode();
		}*/
		
		
		//return (0);
        
        //GeneticSolver*  gsolver = 0;
        
        //gsolver = new GeneticSolver(*apVRPTW, 8);
        //gsolver->execute();
        //return (0);
        
        //sol.complete();
        //std::cout << sol.toString() << std::endl;
        //std::cout << sol.toString() << std::endl;
        //return (0);
        
        //sa = new SimulatedAnnealing(*apVRPTW);
        //sa->execute();
        //return (0);
        
        RandomSolver*	gensolver = 0;
        HuffTree*	htree = 0;
        
        //gensolver = new RandomSolver(*apVRPTW, 2);
        //gensolver->execute();
        
        Tournee		t(*apVRPTW, 0);
        Tournee     t2(*apVRPTW, 0);
        
        /*for (unsigned i = 0 ; i < 8 ; i++)
        {
			t.insertClient(Tournee::INSERT_END, i);
		}
		
		//std::cout << t.toString() << std::endl;
		//t2 = t;
        //std::cout << t2.toString() << std::endl;
        //return (0);
        
        t.moveRandomIntra3Opt();
		std::cout << t.toString() << std::endl;
        */
        
        solver = new BasicSolver(*apVRPTW);
        rsolver = new BasicReverseSolver(*apVRPTW);
        msolver = new Merger(*apVRPTW);
        ntsolver = new NiceTrySolver(*apVRPTW);
        nt2solver = new NiceTry2Solver(*apVRPTW);
        vndsolver = new VNDTry(*apVRPTW);
        
        //vndsolver->execute();
        //return (0);
        
        //MultiStart	multi(*apVRPTW);
        //multi.execute();
        
        
        
        //ntsolver->execute();
        //return (0);
        /*
        for (unsigned i = 0 ; i < 25 ; i++)
			base.push_back(i);
		
		random_shuffle(base.begin(), base.end());
		
        htree = new HuffTree(base);
        htree->printDot("huff.dot");
        htree->updateLevels();
        htree->buildSolutions(*apVRPTW, &soleval);
        std::cout << soleval.toString() << std::endl;
        return (0);
        */
        //Tournee		t(*apVRPTW, 0);
        
        //sol.complete();
        
        //ntsolver->execute();
        //return (0);
        //gsolver->execute();
        //return (0);
        
        /*for (unsigned i = 0 ; i < 10 ; ++i)
			t.insertClient(Tournee::INSERT_END, i);
		
		rsolver->execute();
		solver->execute();
		std::cout << solver->getBestSolution().toString() << std::endl;
		std::cout << rsolver->getBestSolution().toString() << std::endl;
		return (0);
		*/
		
		/*for (unsigned i = 0 ; i < 100 ; i++)
			msolver->getTable().push_back(i);
		msolver->execute();
		return (0);*/
		//t.insertClient(Tournee::INSERT_BEFORE, apVRPTW->buildNewRDepot(), 2);
		//std::cout << t.toString() << std::endl;
		
		//return (0);
		
        //return (0);
        /*sol.complete();
        
        solver->execute();
        solver->getBestSolution().printTournee("tournee0.dot", 1);
        return (0);
        */
        
        //gensolver = new RandomSolver(*apVRPTW, 12);
        //gensolver->execute();
        //return (0);
        //rsolver->execute();
        //rsolver->getBestSolution().fillDeque(base);
        
        for (unsigned i = 0 ; i < 100 ; i++)
            base.push_back(i);
        
        nmsolver = new NiceMergeSolver(*apVRPTW, base, 80000000);
        nmsolver->execute();
        nmsolver->getBestSolution().printTournee("tournee0.dot", 0);
        return (0);
        
        //gensolver->execute();
        
        std::cout << nmsolver->getBestSolution().toString() << std::endl;
        nmsolver->getBestSolution().eval(&soleval);
        std::cout << soleval.toString() << std::endl;
        //nt2solver->execute();
        
        return (0);
        
        for (unsigned i = 0 ; i < 100 ; i++)
            msolver->getTable().push_back(100 - 1 - i);
        //ntsolver->execute();
        //solver->execute();
        msolver->execute();
        //std::cout << msolver->getBestSolution().toString() << std::endl;
        sol.complete();

        
        solver->execute();
        std::cout << solver->getBestSolution().toString() << std::endl;
        solver->getBestSolution().eval(&soleval);
        std::cout << soleval.toString() << std::endl;
        msolver->execute();
        std::cout << msolver->getBestSolution().toString() << std::endl;
        msolver->getBestSolution().eval(&soleval);
        std::cout << soleval.toString() << std::endl;
        rsolver->execute();
        std::cout << rsolver->getBestSolution().toString() << std::endl;
        rsolver->getBestSolution().eval(&soleval);
        std::cout << soleval.toString() << std::endl;
        
        //return (0);
        
        ntsolver->execute();
        std::cout << ntsolver->getBestSolution().toString() << std::endl;
        
        return (0);
        
        
        solver->execute();
        msolver->execute();
        std::cout << msolver->getBestSolution().toString() << std::endl;
        return (0);
        solver->execute();
        std::cout << "BASIC SOLVER" << std::endl;
        std::cout << solver->getBestSolution().toString() << std::endl;
        solver->getBestSolution().eval(&soleval);
        std::cout << soleval.toString() << std::endl;
        
        std::cout << "BASIC REVERSE SOLVER" << std::endl;
        rsolver->execute();
        std::cout << rsolver->getBestSolution().toString() << std::endl;
        rsolver->getBestSolution().eval(&soleval);
        std::cout << soleval.toString() << std::endl;
        
        
        solver->getBestSolution().fillDeque(huff_from_sol);
        
        std::cout << "deque ";
        for (unsigned i = 0 ; i < 100 ; i++)
			std:: cout << huff_from_sol[i] << ":";
			
		std::cout << std::endl;
        
        for (unsigned i = 100 ; i < 0x200 ; i++)
			huff_from_sol.push_back(i);
        
        HuffTree	h(huff_from_sol);
        h.updateLevels();
        h.buildSolutions(*apVRPTW, &soleval);
        std::cout << soleval.toString() << std::endl;
        huff_from_sol.clear();
        rsolver->getBestSolution().fillDeque(huff_from_sol);
        for (unsigned i = 100 ; i < 0x200 ; i++)
			huff_from_sol.push_back(i);
			
		HuffTree	hr(huff_from_sol);
		hr.updateLevels();
		hr.buildSolutions(*apVRPTW, &soleval);
        //h.buildSolutions(*apVRPTW, &soleval);
        
        std::cout << soleval.toString() << std::endl;
        return (0);
        
        /*
        Solution    s(*apVRPTW);
        TourneeEvalResult   evalres;
        Tournee&    t = s[0];
        
        
        t.insertClient(Tournee::INSERT_END, 0);
        t.insertClient(Tournee::INSERT_END, 14);
        t.insertClient(Tournee::INSERT_END, 16);
        t.insertClient(Tournee::INSERT_END, 17);
        std::cout << t.toString() << std::endl;
        
        t.eval(&evalres);
        std::cout << evalres.toString() << std::endl;
        
        return (0);
        */
        
        /*
        unsigned k = 0;
        unsigned current;
        unsigned next;
        
        
        clients_served[DEPOT] = 1;
        current = DEPOT;
        is = apVRPTW->getDistanceByClient(current).begin();
        ++is;
        
        while (clients_served.find(is->second) != clients_served.end())
            ++is;
        
        huff_base[0] = rand() % 100;
        clients_served[huff_base[0]] = 1;
        current = huff_base[0];
        k = 1;
        
        while (k < 100)
        {
            is = apVRPTW->getDistanceByClient(current).begin();
            ++is;
            
            while (clients_served.find(is->second) != clients_served.end())
                ++is;
            huff_base[k] = is->second;
            clients_served[is->second] = 1;
            current = is->second;
            k++;
        }
                
        for (unsigned i = 100 ; i < 0x200 ; i++)
            huff_base[i] = i;
        
        HuffTree    h(huff_base);
        HuffTree	hbest(huff_base);
        SolutionEval	solevalbest;
        
        h.updateLevels();
        h.buildSolutions(*apVRPTW, &soleval);
        //h.buildSolutions(*apVRPTW, &soleval);
        
        std::cout << soleval.toString() << std::endl;
        return (0);
        
        hbest = h;
        hbest.updateLevels();
        hbest.buildSolutions(*apVRPTW, &solevalbest);
        
        //return (0);
        
        for (unsigned i = 0 ; i < 50 ; i++)
        {
			unsigned	r;
			
			h = hbest;
			soleval = solevalbest;
			std::cout << "update levels" << std::endl;
			h.updateLevels();
			std::cout << "build solution call" << std::endl;
			h.buildSolutions(*apVRPTW, &soleval);
			
			if (soleval < solevalbest)
			{
				hbest = h;
				solevalbest = soleval;
			}
		}
        
        std::cout << solevalbest.toString() << std::endl;
        
        //h.updateLevels();
        //h.printDot("init_dot.dot");
        //h.buildSolutions(*apVRPTW);

        
        
        //Savings     savings(*apVRPTW);
        
        //savings.execute();
        */
        
    }
    catch(Exc& e)
    {
        e.printStackTrace();
        e.printMsg();
        return(EXIT_FAILURE);
    }
    
    return (EXIT_SUCCESS);
}
